import tkinter as tk
from tkinter import ttk
from typing import Optional
from ttkbootstrap import Style
from PIL import Image, ImageTk
import numpy as np

class TTKPanel:
    """
    使用 ttkbootstrap 构建的简单GUI：左侧显示视频帧，右侧显示角度/开合与日志。
    通过 StringVar 与 Treeview 实时更新。
    """
    def __init__(self, title: str = "手臂控制预览", theme: str = "darkly"):
        self.root = tk.Tk()
        self.style = Style(theme=theme)
        self.root.title(title)
        self.root.geometry("1400x800")

        # 顶部工具栏：控制开关
        self.toolbar = ttk.Frame(self.root)
        self.toolbar.pack(side=tk.TOP, fill=tk.X, padx=8, pady=6)

        # 复位按钮
        self.reset_requested = False
        self.reset_btn = ttk.Button(self.toolbar, text="复位", command=self._on_reset_click)
        self.reset_btn.pack(side=tk.LEFT, padx=(4, 8))

        # 屏蔽映射节点控件
        self.block_vars = {
            "gripper": tk.BooleanVar(value=False),
            "shoulder": tk.BooleanVar(value=False),
            "elbow": tk.BooleanVar(value=False),
            "wrist": tk.BooleanVar(value=False),
        }
        self.block_frame = ttk.Frame(self.toolbar)
        ttk.Label(self.block_frame, text="屏蔽节点:").pack(side=tk.LEFT)
        ttk.Checkbutton(self.block_frame, text="抓夹", variable=self.block_vars["gripper"]).pack(side=tk.LEFT, padx=2)
        ttk.Checkbutton(self.block_frame, text="肩", variable=self.block_vars["shoulder"]).pack(side=tk.LEFT, padx=2)
        ttk.Checkbutton(self.block_frame, text="肘", variable=self.block_vars["elbow"]).pack(side=tk.LEFT, padx=2)
        ttk.Checkbutton(self.block_frame, text="腕", variable=self.block_vars["wrist"]).pack(side=tk.LEFT, padx=2)
        self.block_frame.pack(side=tk.LEFT, padx=(8, 8))

        # 控制发送开关
        self.control_var = tk.BooleanVar(value=True)
        self.control_btn = ttk.Checkbutton(self.toolbar, text="控制发送", variable=self.control_var)
        self.control_btn.pack(side=tk.RIGHT)

        # 主区：左右分栏
        self.main = ttk.Panedwindow(self.root, orient=tk.HORIZONTAL)
        self.main.pack(fill=tk.BOTH, expand=True)

        # 左侧图像
        self.left = ttk.Frame(self.main)
        self.main.add(self.left, weight=3)
        self.image_label = ttk.Label(self.left)
        self.image_label.pack(fill=tk.BOTH, expand=True, padx=8, pady=8)
        self.photo = None  # 保持引用

        # 右侧信息
        self.right = ttk.Frame(self.main)
        self.main.add(self.right, weight=2)

        self.title_info = ttk.Label(self.right, text="角度与开合", anchor=tk.W)
        self.title_info.pack(fill=tk.X, padx=10, pady=(10, 4))
        self.shoulder_var = tk.StringVar(value="右肩角度: N/A")
        self.elbow_var = tk.StringVar(value="右肘角度: N/A")
        self.wrist_var = tk.StringVar(value="右腕角度: N/A")
        self.open_var = tk.StringVar(value="右手张开度: N/A")
        ttk.Label(self.right, textvariable=self.shoulder_var).pack(anchor=tk.W, padx=16)
        ttk.Label(self.right, textvariable=self.elbow_var).pack(anchor=tk.W, padx=16)
        ttk.Label(self.right, textvariable=self.wrist_var).pack(anchor=tk.W, padx=16)
        ttk.Label(self.right, textvariable=self.open_var).pack(anchor=tk.W, padx=16)

        ttk.Separator(self.right, orient=tk.HORIZONTAL).pack(fill=tk.X, padx=10, pady=10)

        # 参数控制滑杆
        self.params_frame = ttk.Labelframe(self.right, text="控制参数")
        self.params_frame.pack(fill=tk.X, padx=10, pady=6)
        self.deadband_var = tk.DoubleVar(value=2.0)
        ttk.Label(self.params_frame, text="死区%").grid(row=0, column=0, sticky=tk.W)
        ttk.Scale(self.params_frame, from_=0.0, to=10.0, variable=self.deadband_var, orient=tk.HORIZONTAL).grid(row=0, column=1, sticky="ew")
        self.smooth_var = tk.DoubleVar(value=0.3)
        ttk.Label(self.params_frame, text="平滑α").grid(row=0, column=2, sticky=tk.W)
        ttk.Scale(self.params_frame, from_=0.0, to=1.0, variable=self.smooth_var, orient=tk.HORIZONTAL).grid(row=0, column=3, sticky="ew")
        self.maxstep_var = tk.DoubleVar(value=5.0)
        ttk.Label(self.params_frame, text="最大步%").grid(row=1, column=0, sticky=tk.W)
        ttk.Scale(self.params_frame, from_=0.5, to=20.0, variable=self.maxstep_var, orient=tk.HORIZONTAL).grid(row=1, column=1, sticky="ew")
        self.relthr_var = tk.DoubleVar(value=5.0)
        ttk.Label(self.params_frame, text="相对阈%").grid(row=1, column=2, sticky=tk.W)
        ttk.Scale(self.params_frame, from_=0.0, to=20.0, variable=self.relthr_var, orient=tk.HORIZONTAL).grid(row=1, column=3, sticky="ew")
        self.relstep_var = tk.DoubleVar(value=5.0)
        ttk.Label(self.params_frame, text="相对步%").grid(row=2, column=0, sticky=tk.W)
        ttk.Scale(self.params_frame, from_=0.5, to=20.0, variable=self.relstep_var, orient=tk.HORIZONTAL).grid(row=2, column=1, sticky="ew")
        self.center_var = tk.DoubleVar(value=50.0)
        ttk.Label(self.params_frame, text="中心%").grid(row=2, column=2, sticky=tk.W)
        ttk.Scale(self.params_frame, from_=0.0, to=100.0, variable=self.center_var, orient=tk.HORIZONTAL).grid(row=2, column=3, sticky="ew")
        self.shrink_var = tk.DoubleVar(value=0.2)
        ttk.Label(self.params_frame, text="缩小系数").grid(row=3, column=0, sticky=tk.W)
        ttk.Scale(self.params_frame, from_=0.0, to=1.0, variable=self.shrink_var, orient=tk.HORIZONTAL).grid(row=3, column=1, sticky="ew")

        # 上下限滑杆
        self.limits_frame = ttk.Labelframe(self.right, text="上下限(%)")
        self.limits_frame.pack(fill=tk.X, padx=10, pady=6)

        def _mk_limit_row(row, label):
            min_var = tk.DoubleVar(value=0.0)
            max_var = tk.DoubleVar(value=100.0)
            ttk.Label(self.limits_frame, text=label).grid(row=row, column=0, sticky=tk.W)
            ttk.Label(self.limits_frame, text="min").grid(row=row, column=1, sticky=tk.W)
            ttk.Scale(self.limits_frame, from_=0.0, to=100.0, variable=min_var, orient=tk.HORIZONTAL).grid(row=row, column=2, sticky="ew")
            ttk.Label(self.limits_frame, text="max").grid(row=row, column=3, sticky=tk.W)
            ttk.Scale(self.limits_frame, from_=0.0, to=100.0, variable=max_var, orient=tk.HORIZONTAL).grid(row=row, column=4, sticky="ew")
            return (min_var, max_var)

        self.limit_vars = {
            "gripper": _mk_limit_row(0, "抓夹"),
            "shoulder": _mk_limit_row(1, "肩"),
            "elbow": _mk_limit_row(2, "肘"),
            "wrist": _mk_limit_row(3, "腕"),
        }

        for frm in (self.params_frame, self.limits_frame):
            for c in range(0, 5):
                frm.grid_columnconfigure(c, weight=1)

        self.title_logs = ttk.Label(self.right, text="指令控制日志", anchor=tk.W)
        self.title_logs.pack(fill=tk.X, padx=10, pady=(4, 4))
        self.logs = ttk.Treeview(self.right, columns=("time", "status", "summary"), show="headings", height=20)
        self.logs.heading("time", text="时间")
        self.logs.heading("status", text="状态")
        self.logs.heading("summary", text="摘要")
        self.logs.column("time", width=90, anchor=tk.W)
        self.logs.column("status", width=60, anchor=tk.CENTER)
        self.logs.column("summary", width=320, anchor=tk.W)
        self.logs.pack(fill=tk.BOTH, expand=True, padx=10, pady=8)

    def get_filter_params(self) -> dict:
        return {
            "deadband_pct": float(self.deadband_var.get()),
            "max_step_pct": float(self.maxstep_var.get()),
            "smoothing_alpha": float(self.smooth_var.get()),
            "rel_threshold_pct": float(self.relthr_var.get()),
            "rel_step_pct": float(self.relstep_var.get()),
        }
    def get_safe_params(self) -> dict:
        return {
            "center_pct": float(self.center_var.get()),
            "shrink_pct": float(self.shrink_var.get()),
        }
    def get_limits_params(self) -> dict:
        out = {}
        for k, (min_var, max_var) in self.limit_vars.items():
            mn = float(min_var.get())
            mx = float(max_var.get())
            out[k] = (mn, mx)
        return out
    def get_block_nodes(self) -> dict:
        return {
            "gripper": bool(self.block_vars["gripper"].get()),
            "shoulder": bool(self.block_vars["shoulder"].get()),
            "elbow": bool(self.block_vars["elbow"].get()),
            "wrist": bool(self.block_vars["wrist"].get()),
        }

    def consume_reset_request(self) -> bool:
        flag = bool(self.reset_requested)
        self.reset_requested = False
        return flag

    def get_control_enabled(self) -> bool:
        return bool(self.control_var.get())

    def _on_reset_click(self):
        self.reset_requested = True

    def update_info(self, shoulder: Optional[float], elbow: Optional[float], wrist: Optional[float], openness: float):
        s_txt = f"右肩角度: {shoulder:.1f}°" if shoulder is not None else "右肩角度: N/A"
        e_txt = f"右肘角度: {elbow:.1f}°" if elbow is not None else "右肘角度: N/A"
        w_txt = f"右腕角度: {wrist:.1f}°" if wrist is not None else "右腕角度: N/A"
        o_txt = f"右手张开度: {openness:.1f}%"
        self.shoulder_var.set(s_txt)
        self.elbow_var.set(e_txt)
        self.wrist_var.set(w_txt)
        self.open_var.set(o_txt)

    def update_logs(self, log_items):
        # 清空后追加最近12条
        for iid in self.logs.get_children():
            self.logs.delete(iid)
        for item in (log_items[-12:] if log_items else []):
            ok = "OK" if item.get("ok") else "ERR"
            self.logs.insert("", tk.END, values=(item.get("ts", ""), ok, item.get("summary", "")))

    def update_frame(self, frame: np.ndarray):
        # 将OpenCV图像转换并显示在Label
        rgb = frame[:, :, ::-1]  # BGR->RGB
        im = Image.fromarray(rgb)
        self.photo = ImageTk.PhotoImage(image=im)
        self.image_label.configure(image=self.photo)

    def tick(self):
        self.root.update_idletasks()
        self.root.update()

    def close(self):
        self.root.destroy()

    def apply_controller_defaults(self, controller):
        try:
            self.deadband_var.set(float(getattr(controller, "deadband_pct", 2.0)))
            self.maxstep_var.set(float(getattr(controller, "max_step_pct", 5.0)))
            self.smooth_var.set(float(getattr(controller, "smoothing_alpha", 0.3)))
            self.relthr_var.set(float(getattr(controller, "rel_threshold_pct", 5.0)))
            self.relstep_var.set(float(getattr(controller, "rel_step_pct", 5.0)))
            self.center_var.set(float(getattr(controller, "safe_center_pct", 50.0)))
            self.shrink_var.set(float(getattr(controller, "safe_shrink_pct", 0.2)))
            limits = getattr(controller, "limits_pct", None)
            if isinstance(limits, dict):
                for k in ("gripper", "shoulder", "elbow", "wrist"):
                    if k in limits and isinstance(limits[k], (list, tuple)) and len(limits[k]) >= 2:
                        mn = float(limits[k][0]); mx = float(limits[k][1])
                        self.limit_vars[k][0].set(mn)
                        self.limit_vars[k][1].set(mx)
        except Exception:
            pass